package com.onlyxiahui.extend.query.hibernate.syntax.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.util.StringUtils;

import com.onlyxiahui.extend.query.hibernate.QueryWrapper;
import com.onlyxiahui.extend.query.hibernate.syntax.annotation.type.BindType;
import com.onlyxiahui.extend.query.hibernate.syntax.data.BracketInfo;
import com.onlyxiahui.extend.query.hibernate.syntax.data.EquationType;
import com.onlyxiahui.extend.query.hibernate.syntax.data.OrderByInfo;
import com.onlyxiahui.extend.query.hibernate.syntax.data.QueryColumn;
import com.onlyxiahui.extend.query.hibernate.syntax.data.QueryHandleUtil;
import com.onlyxiahui.extend.query.hibernate.syntax.util.data.QueryHqlCondition;
import com.onlyxiahui.extend.query.hibernate.util.DbStringUtil;

/**
 * Description <br>
 * Date 2020-05-27 17:53:38<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class BaseHqlUtil {

	public static String getWhereHqlByColumns(String alias, QueryWrapper qw, List<QueryColumn> columns, List<BracketInfo> brackets) {
		StringBuilder hql = new StringBuilder();
		List<QueryHqlCondition> list = new ArrayList<>();
		if ((null == columns || columns.isEmpty()) && (null == brackets || brackets.isEmpty())) {
			Map<String, Object> map = (null == qw) ? null : qw.getParameterMap();
			if (hasValue(qw)) {
				columns = new ArrayList<>();
				for (Object key : map.keySet()) {
					if (null != key) {
						QueryColumn qc = QueryHandleUtil.getQueryColumn(key.toString(), null, null);
						columns.add(qc);
					}
				}
			}
		}
		sortEquations(columns);
		List<QueryHqlCondition> cs = getColumnQueryHqlConditions(alias, qw, columns);
		List<QueryHqlCondition> bs = getBracketQueryHqlConditions(alias, qw, brackets);
		list.addAll(cs);
		list.addAll(bs);
		if (!list.isEmpty()) {
			String condition = bind(list);
			hql.append("where");
			hql.append(" ");
			hql.append(condition);
		}
		return hql.toString();
	}

	public static List<QueryHqlCondition> getBracketQueryHqlConditions(String alias, QueryWrapper qw, List<BracketInfo> brackets) {
		List<QueryHqlCondition> list = new ArrayList<>();
		if (hasValue(qw) && null != brackets && !brackets.isEmpty()) {
			for (BracketInfo bi : brackets) {
				String bind = DbStringUtil.isBlank(bi.getBind()) ? BindType.and.getValue() : bi.getBind();
				List<QueryColumn> columns = bi.getColumns();
				List<QueryHqlCondition> cs = getColumnQueryHqlConditions(alias, qw, columns);
				if (!cs.isEmpty()) {
					String condition = bind(cs);

					StringBuilder hql = new StringBuilder();
					hql.append("(");
					hql.append(" ");
					hql.append(condition);
					hql.append(" ");
					hql.append(")");

					QueryHqlCondition qhc = new QueryHqlCondition();
					qhc.setBind(bind);
					qhc.setCondition(hql.toString());
					list.add(qhc);
				}
			}
		}
		return list;
	}

	public static String bind(List<QueryHqlCondition> list) {
		StringBuilder hql = new StringBuilder();
		if (null != list) {
			sortBinds(list);
			int i = 0;
			for (QueryHqlCondition v : list) {
				String bind = v.getBind();
				String condition = v.getCondition();
				i++;
				if (i != 1) {
					hql.append(" ");
					hql.append(bind);
					hql.append(" ");
				}
				hql.append(condition);
				hql.append(" ");
			}
		}
		return hql.toString();
	}

	public static void sortBinds(List<QueryHqlCondition> list) {
		if (null != list) {
			list.sort(new Comparator<QueryHqlCondition>() {

				@Override
				public int compare(QueryHqlCondition o1, QueryHqlCondition o2) {
					String t1 = o1.getBind() == null ? "" : o1.getBind();
					String t2 = o2.getBind() == null ? "" : o2.getBind();
					return t1.compareTo(t2);
				}
			});
		}
	}

	public static void sortEquations(List<QueryColumn> list) {
		if (null != list) {
			list.sort(new Comparator<QueryColumn>() {

				@Override
				public int compare(QueryColumn o1, QueryColumn o2) {
					Integer t1 = EquationType.getRank(o1.getEquation());
					Integer t2 = EquationType.getRank(o2.getEquation());
					return t1.compareTo(t2);
				}
			});
		}
	}

	public static List<QueryHqlCondition> getColumnQueryHqlConditions(String alias, QueryWrapper qw, List<QueryColumn> columns) {
		List<QueryHqlCondition> list = new ArrayList<>();

		Map<String, Object> map = (null == qw) ? null : qw.getParameterMap();
		if (hasValue(qw) && null != columns && !columns.isEmpty()) {
			String placeholder = "$.";
			for (QueryColumn qc : columns) {

				String name = qc.getParameter();
				Object data = map.get(name);

				boolean isNull = (null == data);
				boolean isBlank = isNull || (data instanceof String && (StringUtils.isEmpty(data)));
				boolean isNotBlank = !isBlank;
				boolean isCollection = (data instanceof Collection);
				boolean isNotEmpty = isCollection && (!((Collection<?>) data).isEmpty());

				boolean has = (isNotBlank && !isCollection) || isNotEmpty;

				if (has) {
					StringBuilder hql = new StringBuilder();
					String p = "";
					String s = "";
					String defaultEquation = isCollection ? EquationType.in.getValue() : EquationType.eq.getValue();
					if (isCollection) {
						p = ("(");
						s = (")");
					}
					String column = DbStringUtil.isBlank(qc.getColumn()) ? name : qc.getColumn();
					String equation = DbStringUtil.isBlank(qc.getEquation()) ? defaultEquation : qc.getEquation();
					String bind = DbStringUtil.isBlank(qc.getBind()) ? BindType.and.getValue() : qc.getBind();

					boolean hasPlaceholder = column.indexOf(placeholder) >= 0;
					if (null != alias && !alias.isEmpty()) {
						if (hasPlaceholder) {
							column = column.replace(placeholder, alias + ".");
						} else {
							hql.append(alias);
							hql.append(".");
						}
					} else {
						if (hasPlaceholder) {
							column = column.replace(placeholder, alias + ".");
						}
					}

					hql.append(column);
					hql.append(" ");
					hql.append(equation);
					hql.append(p);
					hql.append(" ");
					hql.append(":");
					hql.append(name);
					hql.append(" ");
					hql.append(s);

					QueryHqlCondition qhc = new QueryHqlCondition();
					qhc.setBind(bind);
					qhc.setCondition(hql.toString());

					list.add(qhc);
				}
			}
		}
		return list;
	}

	public static boolean hasValue(QueryWrapper qw) {
		boolean has = false;
		Map<String, Object> map = (null == qw) ? null : qw.getParameterMap();
		if (null != map && !map.isEmpty()) {
			for (Object o : map.values()) {
				boolean isBlank = (null == o) || (o instanceof String && (StringUtils.isEmpty(o)));
				boolean isNotBlank = !isBlank;
				if (isNotBlank) {
					if (o instanceof Collection) {
						boolean isEmpty = ((Collection<?>) o).isEmpty();
						if (!isEmpty) {
							has = true;
							break;
						}
					} else {
						has = true;
						break;
					}
				}
			}
		}
		return has;
	}

	public static String getOrderByHql(String alias, List<OrderByInfo> orderByInfos) {

		StringBuilder hql = new StringBuilder();
		if (null != orderByInfos && !orderByInfos.isEmpty()) {

			List<OrderByInfo> checks = new ArrayList<>();
			for (OrderByInfo orderByInfo : orderByInfos) {
				if ((null != orderByInfo) && null != orderByInfo.getOrderByColumns() && !orderByInfo.getOrderByColumns().isEmpty()) {
					checks.add(orderByInfo);
				}
			}

			if (!checks.isEmpty()) {
				hql.append(" order by ");

				int allSize = checks.size();
				int allLast = allSize - 1;
				for (int j = 0; j < allSize; j++) {
					OrderByInfo orderByInfo = checks.get(j);

					List<String> orderByColumns = orderByInfo.getOrderByColumns();
					String orderBySort = orderByInfo.getSort();

					int size = orderByColumns.size();
					int last = size - 1;

					for (int i = 0; i < size; i++) {
						if (null != alias && !alias.isEmpty()) {
							hql.append(alias);
							hql.append(".");
						}
						hql.append(orderByColumns.get(i));
						if (i < last) {
							hql.append(",");
						}
					}

					if (null != orderBySort && !orderBySort.isEmpty()) {
						hql.append(" ");
						hql.append(orderBySort);
					}

					if (j < allLast) {
						hql.append(",");
					}
				}
			}
		}
		return hql.toString();
	}

	public static String getWhereHqlByParameterValue(String alias, QueryWrapper qw, Map<String, QueryColumn> columnMap) {

		StringBuilder sb = new StringBuilder();

		List<QueryHqlCondition> list = new ArrayList<>();

		Map<String, Object> map = (null == qw) ? null : qw.getParameterMap();
		if (hasValue(qw)) {
			Set<String> keySet = map.keySet();
			int size = keySet.size();

			if (size > 0) {
				for (String name : keySet) {
					Object data = map.get(name);

					boolean isNull = (null == data);
					boolean isBlank = isNull || (data instanceof String && (StringUtils.isEmpty(data)));
					boolean isNotBlank = !isBlank;
					boolean isCollection = (data instanceof Collection);
					boolean isNotEmpty = isCollection && (!((Collection<?>) data).isEmpty());

					boolean has = (isNotBlank && !isCollection) || isNotEmpty;

					if (has) {
						StringBuilder hql = new StringBuilder();
						String p = "";
						String s = "";
						String defaultEquation = isCollection ? EquationType.in.getValue() : EquationType.eq.getValue();
						if (isCollection) {
							p = ("(");
							s = (")");
						}

						QueryColumn qc = (null != columnMap) ? columnMap.get(name) : null;
						String column = (null == qc) || DbStringUtil.isBlank(qc.getColumn()) ? name : qc.getColumn();
						String equation = (null == qc) || DbStringUtil.isBlank(qc.getEquation()) ? defaultEquation : qc.getEquation();
						String bind = (null == qc) || DbStringUtil.isBlank(qc.getBind()) ? BindType.and.getValue() : qc.getBind();

						if (null != alias && !alias.isEmpty()) {
							hql.append(alias);
							hql.append(".");
						}

						hql.append(column);
						hql.append(" ");
						hql.append(equation);
						hql.append(p);
						hql.append(" ");
						hql.append(":");
						hql.append(name);
						hql.append(" ");
						hql.append(s);

						QueryHqlCondition qhc = new QueryHqlCondition();
						qhc.setBind(bind);
						qhc.setCondition(hql.toString());

						list.add(qhc);
					}
				}
			}
		}
		if (!list.isEmpty()) {
			String condition = bind(list);
			sb.append("where");
			sb.append(" ");
			sb.append(condition);
		}
		return sb.toString();
	}
}
